#pragma once

#define VERTEX_UBO 0
#define GEOMETRY_UBO 1
#define FRAGMENT_UBO 2

class IRendererEvent;
class IRequest;
class ITypeRenderer;
class RBlendState;
class RRasterizerState;
class RDepthstencilState;
class RSamplerState;
class RMaterial;
class RShader;
class RConst;
class RTexture;
class RFramebuffer;
class RBuffer;
class RVertexArray;
class RFont;
class DynamicSurface;
class DynGeometryMgr;
class RendererStorageMgr;
class TextMgr;
class MaterialMgr;

class SemanticInfo
{
public:
	uint				m_semantic;
	std::string			m_name;
	uint				m_location;
	GLenum				m_type;
	uint				m_length;
	bool				m_normalize;
};

const SemanticInfo	g_ShaderSemantic[] =
{
	{ESemantic::iPosition0,		"Position0",	0, GL_FLOAT,		3,		false},
	{ESemantic::iPosition1,		"Position1",	1, GL_FLOAT,		3,		false},
	{ESemantic::iNormal0,		"Normal0",		2, GL_FLOAT,		3,		false},
	{ESemantic::iNormal1,		"Normal1",		3, GL_FLOAT,		3,		false},
	{ESemantic::iTexcoord0,		"Texcoord0",	4, GL_FLOAT,		2,		false},
	{ESemantic::iTexcoord1,		"Texcoord1",	5, GL_FLOAT,		2,		false},
	{ESemantic::iColor0,		"Color0",		6, GL_FLOAT,		4,		false},
	{ESemantic::iColor1,		"Color1",		7, GL_FLOAT,		4,		false},
	{ESemantic::iInstancing,	"Instancing",	8, GL_FLOAT,		4,		false},
	{ESemantic::iIndices,		"Indices",		15, GL_UNSIGNED_INT,1,		false},
	
	{ESemantic::oColor0,		"FragColor0",	0, GL_FLOAT,		4,		false},
	{ESemantic::oColor1,		"FragColor1",	1, GL_FLOAT,		4,		false},
	{ESemantic::oColor2,		"FragColor2",	2, GL_FLOAT,		4,		false},
	{ESemantic::oColor3,		"FragColor4",	3, GL_FLOAT,		4,		false},
};

class CRenderer:
	public IRenderer,
	public IConstUpdater
{
public:
	CRenderer();
	~CRenderer();

	void				InitializeOpenGL(int res_x, int res_y);
	int					GetOpenGLConstant(EOpenGLConstant::Type type);

	void				GetTypeRenderer(const std::string& name, uint& arg, ITypeRenderer** type);
	void				AddTypeRenderer(ITypeRenderer* typerenderer, const std::string& name, uint arg);
	void				AddRendererEvent(IRendererEvent* event);
	void				AddRequest(void** ret, IRequest* request);	

	const SemanticInfo*	GetSemanticInfo(const std::string& name);
	const SemanticInfo*	GetSemanticInfo(ESemantic::Type semantic);

	void				RenderFrame();

	int					GetResX();
	int					GetResY();

	void				ResetMatrices();

	float*				GetModelMatrix();
	void				SetModelMatrix(const GLM_MAT4x4& matrix);
	
	float*				GetViewMatrix();
	float*				GetProjectionMatrix();
	void				SetCamera(const GLM_MAT4x4& view, const GLM_MAT4x4& proj, float near, float far, const glm::vec3& position);

	void				SetEngineUniform(EEngineUniform::Type type, void* ptn);
	uint				GetEngineUniformSize(EEngineUniform::Type type);

	void				BindMaterial(const RMaterial* material);

	DynGeometryMgr*		GetDynGeometryMgr();
	RendererStorageMgr* GetRendererStorageMgr();
	TextMgr*			GetTextMgr();
	MaterialMgr*		GetMaterialMgr();
	PostprocessMgr*		GetPostprocessMgr();
	QueueMgr*			GetQueueMgr();

	void				Draw(const RVertexArray* vao, ETopology::Type type, uint vertices, uint instances, const RVertexArray* instvao);
	void				DrawOrigin(float length);

	void				ResetRenderStats();
	const RStats&		GetRenderStats();

private:
	int					m_openGLConstants[EOpenGLConstant::Count];

	MaterialMgr*		m_materialMgr;
	TextMgr*			m_textMgr;
	RendererStorageMgr* m_rendererStorageMgr;
	DynGeometryMgr*		m_dynGeometryMgr;
	PostprocessMgr*		m_postprocessMgr;
	QueueMgr*			m_queueMgr;

	void				UpdateConst(uint arg);

	int					m_res_x;
	int					m_res_y;
	
	IMutex*				m_rendererEvents_mutex;
	std::vector<IRendererEvent*> m_rendererEvents;

	IMutex*				m_requests_mutex;
	std::vector<std::pair<void**, IRequest*>> m_requests;

	IMutex*				m_typeRenderers_mutex;
	std::map<std::string, std::pair<uint, ITypeRenderer*>> m_typeRenderers;

	ITimer*				m_timer;
	RStats				m_renderStats;
	/*--------------------------------------------------------------------------*/

	std::map<std::string, SemanticInfo>			m_semantics;

	/*--------------------------------------------------------------------------*/
	
	GLM_MAT4x4*									m_modelMatrix;
	GLM_MAT4x4*									m_modelViewMatrix;
	GLM_MAT4x4*									m_modelViewProjectionMatrix;;
	GLM_MAT4x4*									m_projectionMatrix;
	GLM_MAT4x4*									m_viewMatrix;
	GLM_MAT4x4*									m_viewProjectionMatrix;
	GLM_MAT4x4*									m_invProjectionMatrix;
	GLM_MAT4x4*									m_invViewProjectionMatrix;

	float*										m_cameraFar;
	float*										m_cameraNear;
	glm::vec4*									m_cameraWorldPosition;
	glm::vec4*									m_debugColor;
	/*--------------------------------------------------------------------------*/
	float*										m_uniform_Bumpmapping_TextureMultiplier_1f;
	float*										m_uniform_Bumpmapping_Strength_1f;
	glm::vec4*									m_uniform_ConstantColor_4f;
	/*--------------------------------------------------------------------------*/
};

namespace LuaFunc
{
	void CreateTexture(const std::string& name, int width, int height, const std::string& format);
	void CreateFramebuffer(const std::string& name, const std::string& textures);
	void BindFramebuffer(const std::string& name, const std::string& clear);
	void RenderType(const std::string& type);
	void RenderQuad(const std::string& matname);
	void CopyTexture(const std::string& name);
	void SwapBuffers();
	void ClearColor(float r, float g, float b, float a, char stencil, float depth);
}